const z_base_Dao = require("@/dao/z_base");
const role_Dao = require("@/dao/role");
const userDao = require("@/dao/user.js");
const UserAuthModel = require("@/model/RDBC"); //模型
const UserInfoModel = require("@/model/UserInfo");
const EmailCodeModel = require("@/model/Email.js");
const RDBCModel = require("@/model/RDBC.js");
const { bcryptCheck, md5, bcryptHash } = require("@/utils/encrypt");
const { ErrorCode } = require("@/utils/r/code");
const { IP } = require("@/utils/ip");
const Redis = require("@/utils/redis");
const { KEYS } = require("./z_enter.js");
const myJWT = require("@/utils/jwt.js");
const gin_context = require("@/utils/gin_context.js");
const { cloneDeep } = require("lodash");

const user = {
  async login(req, username, password) {
    //这一步是权限表中根据用户查找
    let userAuth = await z_base_Dao.GetOne(UserAuthModel.UserAuth, {
      username: username,
    });
    console.log(userAuth);
    if (!userAuth) {
      //登录根据用户名查询用户不存在返回错误信息
      return {
        loginVo: {},
        code: ErrorCode.ERROR_USER_NOT_EXIST,
      };
    }

    // 检测密码是否正确
    if (!bcryptCheck(password, userAuth.password)) {
      return {
        loginVo: {},
        code: ErrorCode.ERROR_PASSWORD_WRONG,
      };
    }

    // 获取用户详细信息 DTO
    let userDetailDTO = await this.convertUserDetailDTO(userAuth, req);

    // 登录信息正确, 生成 Token
    // TODO: 目前只给用户设定一个角色, 获取第一个值就行, 后期优化: 给用户设置多个角色
    // UUID 生成方法: ip + 浏览器信息 + 操作系统信息

    let uuid = md5(
      userDetailDTO.LoginVO.IpAddress + userDetailDTO.Browser + userDetailDTO.OS
    );

    //token生成 用户id 角色 uuid
    let token = myJWT.genToken(userAuth.id, userDetailDTO.RoleLabels[0], uuid);
    if (!token) {
      //生成token出错执行
      return {
        loginVo: {},
        code: ErrorCode.ERROR_TOKEN_CREATE,
      };
    }
    //给用户的详细信息绑定token
    userDetailDTO.LoginVO.token = token;

    //目前登录成功，更新的字段
    const data = {
      ip_address: userDetailDTO.LoginVO.IpAddress,
      ip_source: userDetailDTO.LoginVO.IpSource,
      last_login_time: userDetailDTO.LoginVO.LastLoginTime,
    };
    await z_base_Dao.Update(UserAuthModel.UserAuth, data, { id: userAuth.id });

    //转换为json字符串存储
    const sessionInfoStr = JSON.stringify({ userDetailDTO });

    // 将信息保存到 Express 会话
    req.session[KEYS.KEY_USER + uuid] = sessionInfoStr;

    //将数据存储redis中
    await Redis.Set(KEYS.KEY_USER + uuid, sessionInfoStr, 600);

    return {
      loginVo: userDetailDTO.LoginVO,
      code: ErrorCode.OK,
    };
  },
  // 转化 UserDetailDTO
  async convertUserDetailDTO(userAuth, req) {
    //获取ip域名和地址
    let ipAddress = IP.getIpAddress(req); //192.168.153.1
    let ipSource = await IP.getIpSourceSimpleIdle(ipAddress); //内网IP
    let browser = "unkonwn";
    let os = "unknown";
    const userAgent = IP.getUserAgent(req); //获取用户代理
    if (userAgent) {
      // 格式化获取用户登录的操作系统和浏览器
      browser =
        userAgent.family +
        " " +
        userAgent.major +
        "." +
        userAgent.minor +
        "." +
        userAgent.patch;
      os =
        userAgent.os.family +
        " " +
        userAgent.os.major +
        "." +
        userAgent.os.minor +
        "." +
        userAgent.os.patch;
    }
    //获取用户的详细信息,根据用户id查询
    const userInfo = await z_base_Dao.GetOne(UserInfoModel.UserInfo, {
      id: userAuth.id,
    });
    // FIXME: 获取该用户对应的角色, 没有角色默认是 "test"
    const roleLabels = await role_Dao.GetLabelsByUserInfoId(userInfo.id);

    if (roleLabels.length === 0) {
      roleLabels = ["test"];
    }

    // 用户点赞 Set
    const articleLikeSet = await Redis.SMembers(
      KEYS.KEY_ARTICLE_USER_LIKE_SET + userInfo.id.toString()
    );

    const commentLikeSet = await Redis.SMembers(
      KEYS.KEY_COMMENT_USER_LIKE_SET + userInfo.id.toString()
    );
    return {
      LoginVO: {
        ID: userAuth.id,
        UserInfoId: userInfo.id,
        Email: userInfo.email,
        LoginType: userAuth.login_type,
        Username: userAuth.username,
        Nickname: userInfo.nickname,
        Avatar: userInfo.avatar,
        Intro: userInfo.intro,
        Website: userInfo.website,
        IpAddress: ipAddress,
        IpSource: ipSource,
        LastLoginTime: new Date().toLocaleString(),
        ArticleLikeSet: articleLikeSet,
        CommentLikeSet: commentLikeSet,
      },
      Password: userAuth.password,
      RoleLabels: roleLabels,
      IsDisable: userInfo.is_disable,
      Browser: browser,
      OS: os,
    };
  },
  /**
   * 获取用户信息
   * @param {*} id
   * @returns
   */
  async getInfo(id) {
    try {
      //查询数据库
      let data = await z_base_Dao.GetOne(UserInfoModel.UserInfo, {
        id,
      });
      if (!data) {
        throw new Error("user not found");
      }
      const ArticleLikeSet = await Redis.SMembers(
        `${KEYS.KEY_ARTICLE_USER_LIKE_SET}:${id}`
      );
      const CommentLikeSet = await Redis.SMembers(
        `${KEYS.KEY_COMMENT_USER_LIKE_SET}:${id}`
      );
      data.ArticleLikeSet = ArticleLikeSet;
      data.CommentLikeSet = CommentLikeSet;
      return data;
      //  const CommentLikeSet
    } catch (error) {
      console.log(error);
    }
  },
  async Logout(req) {
    const uuid = gin_context.GetFromContext(req, "uuid");

    req.session.destroy(() => {
      console.log("销毁成功");
    });

    //删除redis中缓存
    await Redis.Del(KEYS.KEY_USER + uuid);
    await Redis.Del(KEYS.KEY_BLOG_CONFIG);
    await Redis.Del(KEYS.KEY_ABOUT);
  },
  async GetOnlineList(query) {
    let onlineList = [];
    const keys = await Redis.Keys(KEYS.KEY_USER + "*");
    console.log("GetOnlineList", keys);
  },
  async GetList(query) {
    const count = await userDao.GetCount(query);
    const pageData = await userDao.GetList(query);
    return {
      total: count,
      pageData,
      pageSize: +query.page_size,
      pageNum: +query.page_num,
    };
  },
  async UpdateCurrent(current) {
    const user = cloneDeep(current);
    await z_base_Dao.Update(UserInfoModel.UserInfo, current, {
      id: current.id,
    });
    return ErrorCode.OK;
  },
  async UpdateCurrentPassword(psw, id) {
    const user = await z_base_Dao.GetOne(UserAuthModel.UserAuth, { id: id });

    //检查旧密码是否正确
    if (user && bcryptCheck(psw.old_password, user.password)) {
      user.password = bcryptHash(psw.new_password);
      await z_base_Dao.Update(UserAuthModel.UserAuth, user, { id });
      return ErrorCode.OK;
    } else {
      return ErrorCode.ERROR_OLD_PASSWORD;
    }
  },
  async Register(data) {
    // { email: '2596167160@qq.com', code: '123456 ', password: '123456' }
    //首先需要校验当前邮箱是否被注册
    const existByEmail = await z_base_Dao.GetOne(UserInfoModel.UserInfo, {
      email: data.email,
    });
    if (existByEmail && existByEmail.id) {
      //邮箱存在
      return ErrorCode.ERROR_EMAIL_HAS_EXIST;
    }
    //判断验证码是否过期和相等,根据邮箱查找
    const exsitByCode = await z_base_Dao.GetOne(EmailCodeModel.EmailCode, {
      email: data.email,
      code: data.code,
    });
    if (!exsitByCode) {
      //未找到的情况下，验证码未发送，返回错误信息
      return ErrorCode.ERROR_EMAIL_CODE_NOT_EXIST;
    }
    //时间字符串无法直接比较需要处理
    const prevTime = new Date(exsitByCode.created_at).getTime(); //验证码初次创建的时间
    if (new Date(new Date().toLocaleString()).getTime() - prevTime > 50000) {
      //5s
      return ErrorCode.ERROR_EMAIL_CODE_RUNTIME;
    }
    //校验验证码是否相同,理论不会执行，因为查询条件是邮箱和code
    if (data.code !== exsitByCode.code) {
      return ErrorCode.ERROR_EMAIL_CODE_NOT_SAME;
    }
    //密码加密处理
    const newPsw = bcryptHash(data.password);
    //相表中插入数据
    //先插入user_info表
    const userInfo = await z_base_Dao.Create(UserInfoModel.UserInfo, {
      email: data.email,
      nickname: "用户" + data.email,
    });
    //插入user_role表，默认权限用户
    await z_base_Dao.Create(RDBCModel.UserRole, {
      user_id: userInfo.id,
      role_id: 2,
    });
    //插入user_auth表
    await z_base_Dao.Create(RDBCModel.UserAuth, {
      user_info_id: userInfo.id,
      username: data.email,
      password: newPsw,
    });
    return ErrorCode.OK;
  },
};

module.exports = user;
